using System;
using System.IO;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Threading;

using SpringProxAPI;
using SproxDesFireAPI;


namespace MyCard
{
  public class fMain:System.Windows.Forms.Form
  {
#region Card data

    private const int CARD_UNKNOWN = -1;
    private const int CARD_NONE = 0;
    private const int CARD_MIFARE_1K = 1;
    private const int CARD_MIFARE_4K = 2;
    private const int CARD_MIFARE_UL = 3;
    private const int CARD_DESFIRE = 4;
    private const int CARD_JAYCOS = 5;

    public bool card_is_tcl;
    public bool card_is_type_a;
    public bool card_is_type_b;
    public int card_is_type;

    public byte[] card_iso_a_atq = new byte[2];
    public byte[] card_iso_a_sak = new byte[1];
    public byte[] card_iso_a_snr = new byte[12];
    public byte card_iso_a_snrlen;
    public byte[] card_iso_b_atq = new byte[11];

    public byte[] card_tcl_ats = new byte[32];
    public byte card_tcl_atslen;

    public string card_userid;
    public string card_lastname;
    public string card_firstname;
    public string card_birthdate;

    public ushort card_picture_size;
    public byte[] card_picture_data = new byte[3072];

    public byte card_finger_type;

    public byte card_finger_size_1;
    public byte[] card_finger_data_1 = new byte[170];
    public byte card_finger_size_2;
    public byte[] card_finger_data_2 = new byte[170];

#endregion

#region Worker (background thread)

    private Thread workerThread;
    private ManualResetEvent workerEvent;
    private bool workerAllowed;
    private bool workerRunning;
    private int workerNextTask;
    private int workerDoneTask;
    private bool workerResult;
    private bool readerActive;
    private bool readerHasFingerprint;

    private const int WORK_NONE = 0;
    private const int WORK_OPEN_READER = 1;
    private const int WORK_FIND_CARD = 2;
    private const int WORK_READ_CARD = 3;
    private const int WORK_ERASE_CARD = 4;
      private System.Windows.Forms.Label lbBirth;
    private System.Windows.Forms.Label lbUserID;
      private PictureBox branded;
      private PictureBox header;
    private const int WORK_VERIFY = 5;

    private bool OpenReaderTask()
    {
      short rc;

      if (readerActive)
          return true;

      /* In case of a communcation error, we must close it before */
        SPROX.ReaderClose();

      /* Try to open the reader */
        rc = SPROX.ReaderOpen("");
      if (rc == SPROX.MI_OK)
      {
        readerActive = true;
        SPROX.ControlLed(0, 0, 0);
        readerHasFingerprint = false;
        uint features = 0;
        if (SPROX.ReaderGetFeatures(ref features) == SPROX.MI_OK)
        {
          if ((features & 0x00000040) != 0)
            readerHasFingerprint = true;
        }
      }

      return readerActive;
    }

    private bool ReadCardTask()
    {
      bool rc = false;

      card_userid = "";
      card_lastname = "";
      card_firstname = "";
      card_birthdate = "";

      card_picture_size = 0;
      card_finger_type = 0;
      card_finger_size_1 = 0;
      card_finger_size_2 = 0;

      switch (card_is_type)
      {
        case CARD_MIFARE_1K:
        case CARD_MIFARE_4K:
          rc = ReadCardMifare();
          break;

        case CARD_DESFIRE:
          rc = ReadCardDesfire();
          break;

      }

      /* Power down the antenna to preserve batteries */
      if (SPROX.ControlRF(0) != SPROX.MI_OK)
      {
        /* Fatal error, this one shoul'd never fail ! */
        readerActive = false;
        SPROX.ReaderClose();
        return false;
      }

      return rc;
    }

    private void VerifyAsyncAction(int code)
    {

    }

    private void workerLoop()
    {
      int task;
      bool rc;

      workerRunning = true;
      while (workerAllowed)
      {
        workerEvent.WaitOne();
        if (!workerAllowed)
          break;
        task = workerNextTask;

        /* Until reader is found, the only allowed task is to open it... */
        if (!readerActive)
          task = WORK_OPEN_READER;

        switch (task)
        {
          case WORK_OPEN_READER:
            rc = OpenReaderTask();
            break;
          case WORK_FIND_CARD:
            rc = FindCardTask();
            break;
          case WORK_READ_CARD:
            rc = ReadCardTask();
            break;
          case WORK_VERIFY:
            rc = VerifyTask();
            break;
          default:
            rc = false;
            break;
        }


        workerResult = rc;
        workerDoneTask = task;
        workerNextTask = 0;
        workerEvent.Reset();
        
        Invoke(new EventHandler(workerCallback));
      }
      workerRunning = false;
    }

    private void workerDone(int task, bool rc)
    {
      switch (task)
      {
        case WORK_OPEN_READER:
          if (rc)
          {
            eStatus.Text = "Reader activated";
          } else
          {
            eStatus.Text = "Reader not available";
          }
          break;

        case WORK_FIND_CARD:
          if (rc)
          {
            switch (card_is_type)
            {
              case CARD_MIFARE_1K:
                eStatus.Text = "Reading Mifare 1k card";
                break;
              case CARD_MIFARE_4K:
                eStatus.Text = "Reading Mifare 4k card";
                break;
              case CARD_DESFIRE:
                eStatus.Text = "Reading Desfire card";
                break;
              default:
                eStatus.Text = "Unsupported card";
                card_is_type = CARD_NONE;
                break;

            }

            if (card_is_type != CARD_NONE)
              workerStart(WORK_READ_CARD);
          } else
          {
            eStatus.Text = "Put your card on the reader";
          }
          break;

        case WORK_READ_CARD:
          lbFingerprintOK.Visible = false;
          lbFingerprintKO.Visible = false;

          if (rc)
          {
            eStatus.Text = "Read success";

            eUserID.Text = card_userid;
            eLastName.Text = card_lastname;
            eFirstName.Text = card_firstname;
            eBirthDate.Text = card_birthdate;

            if (card_picture_size > 0)
            {
              if (pictureBox.Image != null)
              {
                pictureBox.Image.Dispose();
                pictureBox.Image = null;
              }

              string fn = System.IO.Path.GetTempPath() + "MyCard.jpg";
              FileStream fs = File.Create(fn);

              fs.Write(card_picture_data, 0, card_picture_size);
              fs.Flush();
              fs.Close();

              try
              {
                pictureBox.Image = new Bitmap(fn);
                pictureBox.Visible = true;
              }
              catch(Exception)
              {
                pictureBox.Visible = false;
              }
            } else
            {
              pictureBox.Visible = false;
            }

            if (readerHasFingerprint)
            {
              if ((card_finger_size_1 > 0) || (card_finger_size_2 > 0))
              {
                workerStart(WORK_VERIFY);
              }
            }

            /* Next run in 5 seconds */
            tmrBasetime.Interval = 5000;
          } else
          {
            eStatus.Text = "Read failed";

            eUserID.Text = "";
            eLastName.Text = "";
            eFirstName.Text = "";
            eBirthDate.Text = "";

            pictureBox.Visible = false;

            /* Next run in 1,5 seconds */
            tmrBasetime.Interval = 1500;
          }
          break;

        case WORK_VERIFY:
          if (rc)
          {
            eStatus.Text = "Fingerprint authentication OK";
            lbFingerprintOK.Visible = true;
          } else
          {
            eStatus.Text = "Fingerprint authentication error";
            lbFingerprintKO.Visible = true;
          }
          break;

        default:
          eStatus.Text = "???";
          break;
      }
    }

    private void workerStart(int task)
    {
      workerDoneTask = 0;
      workerNextTask = task;
      workerEvent.Set();
    }
    
    
    private void workerCallback(object sender, EventArgs e)
    {
      if (workerDoneTask != 0)
      {
        workerDone(workerDoneTask, workerResult);
        workerDoneTask = 0;
      }
    }

#endregion

#region MSO fingerprint verification

    /*
     * ILV decoder
     * -----------
     */

    /* Function to loop inside an ILV list */
    /* ----------------------------------- */
    bool ilv_loop(byte[]buffer, ref ushort offset, ref byte tag,
                  ref ushort length, ref short value_pos)
    {
      ushort l;

      if (buffer == null)
        return false;

      if (buffer[offset] == 0x00)
        return false;


      tag = buffer[offset];
      l = buffer[offset + 2];
      l *= 0x0100;
      l += buffer[offset + 1];
      length = l;

      value_pos = (short) (offset + 3);

      offset += 3;
      offset += l;

      if (l > 1024)
        return false;

      return true;
    }

    /*
     * ILV encoder
     * -----------
     */

    /* Start the building the ILV buffer */
    void ilv_init(byte[]buffer, ref ushort size, byte tag)
    {
      /* Command appears on top of the buffer */
      buffer[0] = tag;

      /* Actual length will be written at last */
      buffer[1] = 0;
      buffer[2] = 0;

      /* OK */
      size = 3;
    }

    /* Done with ILV buffer */
    void ilv_final(byte[]buffer, ref ushort size)
    {
      ushort length;
      ushort temp;

      /* Compute length of the VALUE field */
      if (size < 3)
      {
        /* Oups... */
        size = 0;
        return;
      }

      temp = size;
      temp -= 3;
      length = temp;
      //      length = size - 3;

      /* Write actual length */
      buffer[1] = (byte) (length % 0x0100);
      buffer[2] = (byte) (length / 0x0100);
    }

    /* Add a DWORD value to the ILV buffer */
    void ilv_add_dword(byte[]buffer, ref ushort size, uint value1)
    {
      /* Enqueue the WORD (Little Endian) */
      buffer[size + 0] = (byte) (value1 % 0x00000100);
      buffer[size + 1] = (byte) (value1 / 0x00000100);
      buffer[size + 2] = (byte) (value1 / 0x00010000);
      buffer[size + 3] = (byte) (value1 / 0x01000000);

      /* OK */
      size += 4;
    }

    /* Add a WORD value to the ILV buffer */
    void ilv_add_word(byte[]buffer, ref ushort size, ushort value1)
    {
      /* Enqueue the WORD (Little Endian) */
      buffer[size + 0] = (byte) (value1 % 0x0100);
      buffer[size + 1] = (byte) (value1 / 0x0100);

      /* OK */
      size += 2;
    }

    /* Add a BYTE value to the ILV buffer */
    void ilv_add_byte(byte[]buffer, ref ushort size, byte value1)
    {
      buffer[size + 0] = value1;
      size += 1;
    }

    /* Add a data buffer to the ILV buffer */
    void ilv_add_data(byte[]buffer, ref ushort size, byte[]data,
                      ushort length)
    {
      /* Enqueue the buffer */
      for (ushort i = 0; i < length; i++)
        buffer[size + 0 + i] = data[i];
      size += length;
    }

    void VerifyAsyncAction(byte code)
    {
      switch (code)
      {
        case 0:
          eStatus.Text = "Verifying fingerprint...";
          break;
        case 1:
          eStatus.Text = "Move up";
          break;
        case 2:
          eStatus.Text = "Move down";
          break;
        case 3:
          eStatus.Text = "Move left";
          break;
        case 4:
          eStatus.Text = "Move right";
          break;
        case 5:
          eStatus.Text = "Press harder";
          break;
        case 6:
          eStatus.Text = "Move finger";
          break;
        case 7:
          eStatus.Text = "Remove finger";
          break;
        case 8:
          eStatus.Text = "Finger OK";
          break;
      }

      Application.DoEvents();
    }

    private bool VerifyTask()
    {
      byte[]buffer = new byte[1024];
      byte async;
      ushort size;
      ushort length;
      short rc;


      for (int i = 0; i < buffer.Length; i++)
        buffer[i] = 0;
      length = 0;

      /* Initialize ILV */
      ilv_init(buffer, ref length, 0x20); //ILV_VERIFY);

      /* Build ILV */
      ilv_add_word(buffer, ref length, 10); /* Timeout = 10 seconds */
      ilv_add_word(buffer, ref length, 5);  /* Matching threshold = 5 (recommended value) */
      ilv_add_byte(buffer, ref length, 0);  /* Acquisition quality threshold = default (0) */


      /* Add fingers */
      if (card_finger_size_1 > 0)
      {
        ilv_add_byte(buffer, ref length, card_finger_type);
        ilv_add_word(buffer, ref length, card_finger_size_1);
        ilv_add_data(buffer, ref length, card_finger_data_1,
                     card_finger_size_1);
      }
      if (card_finger_size_2 > 0)
      {
        ilv_add_byte(buffer, ref length, card_finger_type);
        ilv_add_word(buffer, ref length, card_finger_size_2);
        ilv_add_data(buffer, ref length, card_finger_data_2,
                     card_finger_size_2);
      }

      /* We want to receive asynchronous events */
      ilv_add_byte(buffer, ref length, 52); //ID_ASYNCHRONOUS_EVENT);
      ilv_add_word(buffer, ref length, 4);
      ilv_add_dword(buffer, ref length, 0x00000001);

      /* Write length in the ILV */
      ilv_final(buffer, ref length);

      /* Send the command to the MSO and process its responses (including asynchronous messages) */
      /* --------------------------------------------------------------------------------------- */
      async = 0;
      for (;;)
      {
        size = (ushort) buffer.Length;

        /* NOTE : the timeout of the exchange must be a little greater than the timeout offered  */
        /*        to the MSO, overwhise the library will return an error code before the MSO has */
        /*        finished is process                                                            */
        if (async != 0)
          rc =
            SPROX.MSO_ExchangeEx(null, 0, buffer, ref size, 12000, ref async);
        else
          rc =
            SPROX.MSO_ExchangeEx(buffer, length, buffer, ref size, 35000,
                                 ref async);

        if (rc != 0)
          return false;         /* Error */

        if (async != 0)
        {
          /* Easy processing of asynchronous message, no error control */
          switch (buffer[4])
          {
            case 0x01:
              VerifyAsyncAction(buffer[7]);
              break;
          }

          /* NOTE : we MUST go back to MSO_Exchange AS SOON AS POSSIBLE because if we were to  */
          /*        miss any message (asynchronous or final result), the reader will report a  */
          /*        protocol error and abort the process                                       */
        } else
        {
          break;                /* Last message -> end of the loop */
        }
      }

      /* 
       * Reply format is :
       *  0      : reply ID
       *  1 -> 2 : reply length
       *  3      : request status
       *  4      : matching result
       */

      /* Check returned reply and status */
      /* ------------------------------- */

      if (buffer[0] != 0x20)    //ILV_VERIFY)
      {
        eStatus.Text = "Wrong MSO reply";
        return false;
      }
      if (buffer[3] != 0x00)
      {
        eStatus.Text = "Wrong MSO status";
        return false;
      }

      /* Check matching result */
      /* --------------------- */

      switch (buffer[4])
      {
        case 1:                //ILVSTS_HIT    : /* Verify -> OK ! */
          eStatus.Text = "Verify -> OK !";
          return true;

        case 2:                //ILVSTS_NO_HIT : /* Verify -> KO ! */
          eStatus.Text = "Verify -> KO !";
          return false;
      }

      eStatus.Text = "Wrong MSO result";
      return false;
    }

#endregion

#region Mifare reading

    public byte[] MIFARE_HEADER =
      new byte[16] { 0x00, 0x00, 0x50, 0x72, 0x6F, 0x2D, 0x41, 0x63, 0x74,
      0x69, 0x76, 0x65, 0x00, 0x11, 0x22, 0x33
    };
    public byte[] MIFARE_KEY_A =
      new byte[6] { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 };

    private bool ReadCardMifare()
    {
      byte[]buffer = new byte[528];
      int i;
      byte sector;
      short rc;

      /* Read the part common to 1k and 4k */
      for (sector = 0; sector < 11; sector++)
      {
        byte[]t = new byte[48];
        rc = SPROX.MifStReadSector(null, sector, t, MIFARE_KEY_A);
        if (rc != SPROX.MI_OK)
          return false;
        for (i = 0; i < 48; i++)
          buffer[48 * sector + i] = t[i];
      }

      /* Decode user data */
      for (i = 0; i < 8; i++)
        card_userid += String.Format("{0:X02}", buffer[48 + i]);
      for (i = 0; i < 32; i++)
      {
        if (buffer[48 + 16 + i] == 0)
          break;
        card_lastname += (char) buffer[48 + 16 + i];
      }
      for (i = 0; i < 32; i++)
      {
        if (buffer[48 + 48 + i] == 0)
          break;
        card_firstname += (char) buffer[48 + 48 + i];
      }
      if ((buffer[48 + 15] != 0) && (buffer[48 + 14] != 0)
          && (buffer[48 + 13] != 0))
        card_birthdate =
          String.Format("{0:00}/{1:00}/{2:0000}", buffer[48 + 15],
                        buffer[48 + 14], buffer[48 + 13] + 1900);

      card_picture_size = (ushort) (buffer[48 + 8] * 0x0100 + buffer[48 + 9]);
      card_finger_size_1 = buffer[48 + 10];
      card_finger_size_2 = buffer[48 + 11];
      card_finger_type = buffer[48 + 12];

      /* Retrieve fingerprints */
      if ((card_finger_size_1 > 0) || (card_finger_size_2 > 0))
      {
        for (i = 0; i < 170; i++)
          card_finger_data_1[i] = buffer[128 + i];
        for (i = 0; i < 170; i++)
          card_finger_data_2[i] = buffer[128 + i + 170];
      }

      if ((card_picture_size > 0) && (card_is_type == CARD_MIFARE_4K))
      {
        /* Read the picture */
        int offset = 0;

        for (sector = 11; sector < 40; sector++)
        {
          byte l;

          if (sector < 32)
            l = 48;
          else
            l = 240;
          byte[]t = new byte[l];

          rc = SPROX.MifStReadSector(null, sector, t, MIFARE_KEY_A);
          if (rc != SPROX.MI_OK)
            return false;

          for (i = 0; i < l; i++)
            card_picture_data[offset + i] = t[i];
          offset += l;
        }
      } else
        card_picture_size = 0;

      return true;
    }

#endregion

#region Desfire reading

    public const uint DESFIRE_APPLICATION_ID = 0x00112233;
    public const byte DESFIRE_IDENT_FILE_ID = 0;
    public const byte DESFIRE_PICTURE_FILE_ID = 1;
    public const byte DESFIRE_FINGER_FILE_ID = 2;
    public byte[] DESFIRE_BLANK_KEY =
      new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    public byte[] DESFIRE_KEY_1 =
      new byte[16] { 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33
    };

    private bool ReadCardDesfire()
    {
      byte[]buffer = new byte[4096];
      uint length = 0;
      int i;

      if (SproxDesFire.SelectApplication(DESFIRE_APPLICATION_ID) !=
          SPROX.MI_OK)
        return false;

      if (SproxDesFire.Authenticate(1, DESFIRE_KEY_1) != SPROX.MI_OK)
        return false;

      if (SproxDesFire.ReadData2(DESFIRE_IDENT_FILE_ID, 0, 0, buffer, ref length) != SPROX.MI_OK)
        return false;

      for (i = 0; i < 8; i++)
        card_userid += String.Format("{0:X02}", buffer[i]);
      for (i = 0; i < 32; i++)
      {
        if (buffer[16 + i] == 0)
          break;
        card_lastname += (char) buffer[16 + i];
      }
      for (i = 0; i < 32; i++)
      {
        if (buffer[48 + i] == 0)
          break;
        card_firstname += (char) buffer[48 + i];
      }
      card_birthdate =
        String.Format("{0:00}/{1:00}/{2:0000}", buffer[15], buffer[14],
                      buffer[13] + 1900);

      card_picture_size = (ushort) (buffer[8] * 0x0100 + buffer[9]);
      card_finger_size_1 = buffer[10];
      card_finger_size_2 = buffer[11];
      card_finger_type = buffer[12];

      if (card_picture_size > 0)
      {
        if (SproxDesFire.ReadData2(DESFIRE_PICTURE_FILE_ID, 0, 0, buffer, ref length) != SPROX.MI_OK)
          return false;
        for (i = 0; i < 3072; i++)
          card_picture_data[i] = buffer[i];
      }

      if ((card_finger_size_1 > 0) || (card_finger_size_2 > 0))
      {
        if (SproxDesFire.
            ReadData2(DESFIRE_FINGER_FILE_ID, 0, 0, buffer,
                      ref length) != SPROX.MI_OK)
          return false;
        for (i = 0; i < 170; i++)
          card_finger_data_1[i] = buffer[i];
        for (i = 0; i < 170; i++)
          card_finger_data_2[i] = buffer[i + 170];
      }

      return true;
    }

#endregion

#region ISO/IEC 14443 card detection and selection

    private bool FindCardTask()
    {
      short rc;

      /* We must halt the card first (in case we were already connected) */
      if (card_is_tcl)
      {
        SPROX.Tcl_Deselect(0xFF);
      } else if (card_is_type_a)
      {
        SPROX.A_Halt();
      } else if (card_is_type_b)
      {
        SPROX.B_Halt(card_iso_b_atq);
      }

      card_is_type_a = false;
      card_is_type_b = false;
      card_is_tcl = false;

      if (SPROX.ControlRF(1) != SPROX.MI_OK)
      {
        /* Fatal error, this one shoul'd never fail ! */
        readerActive = false;
        SPROX.ReaderClose();
        return false;
      }

      /* At first try to find an ISO-14443-A card */
      SPROX.SetConfig(SPROX.CFG_MODE_ISO_14443_A);

      card_iso_a_snrlen = 12;
      rc =
        SPROX.A_SelectAny(card_iso_a_atq, card_iso_a_snr,
                          ref card_iso_a_snrlen, card_iso_a_sak);

      if (rc == SPROX.MI_OK)
      {
        /* A card has been found */
        card_is_type = CARD_UNKNOWN;

        if ((card_iso_a_sak[0] & 0x20) != 0)
        {
          /* Card support T=CL, try to activate it */
          card_tcl_atslen = 32;
          rc = SPROX.TclA_GetAts(0xFF, card_tcl_ats, ref card_tcl_atslen);
          if (rc == SPROX.MI_OK)
          {
            /* Found a type A T=CL card */
            card_is_type_a = true;
            card_is_tcl = true;

            if ((card_iso_a_atq[1] == 0x03) && (card_iso_a_atq[0] == 0x44))
            {
              card_is_type = CARD_DESFIRE;
            }
            return true;
          }
        } else
        {
          /* No T=CL support, but type A card found anyway */
          card_is_type_a = true;

          if ((card_iso_a_atq[1] == 0x00) && (card_iso_a_atq[0] == 0x04))
          {
            card_is_type = CARD_MIFARE_1K;
          } else
            if ((card_iso_a_atq[1] == 0x00) && (card_iso_a_atq[0] == 0x02))
          {
            card_is_type = CARD_MIFARE_4K;
          } else
            if ((card_iso_a_atq[1] == 0x00) && (card_iso_a_atq[0] == 0x44))
          {
            card_is_type = CARD_MIFARE_UL;
          }

          return true;
        }

        /* Type A card found but with error */
        goto failed;
      }

      /* Nox try to find an ISO-14443-B card */
      SPROX.SetConfig(SPROX.CFG_MODE_ISO_14443_B);

      rc = SPROX.B_SelectAny(0x00, card_iso_b_atq);

      if (rc == SPROX.MI_OK)
      {
        /* A card has been found */
        card_is_type = CARD_UNKNOWN;

        if ((card_iso_b_atq[10] & 0x01) != 0)
        {
          /* Card support T=CL, try to activate it */
          card_tcl_atslen = 32;
          rc =
            SPROX.TclB_Attrib(card_iso_b_atq, 0xFF, card_tcl_ats,
                              ref card_tcl_atslen);
          if (rc == SPROX.MI_OK)
          {
            /* Found a type B T=CL card */
            card_is_type_b = true;
            card_is_tcl = true;
            return true;
          }
        } else
        {
          /* No T=CL support, but type Bcard found anyway */
          card_is_type_b = true;
          return true;
        }

        /* Type B card found but with error */
        goto failed;
      }

    failed:
      /* Power down the antenna to preserve batteries */
      if (SPROX.ControlRF(0) != SPROX.MI_OK)
      {
        /* Fatal error, this one shoul'd never fail ! */
        readerActive = false;
        SPROX.ReaderClose();
      }
      return false;
    }

#endregion

#region Windows Form Design

    private System.Windows.Forms.TextBox eStatus;
      private System.Windows.Forms.Label lbName;
    private System.Windows.Forms.Label lbDesc;
    private System.Windows.Forms.TextBox eUserID;
    private System.Windows.Forms.TextBox eFirstName;
    private System.Windows.Forms.TextBox eLastName;
    private System.Windows.Forms.TextBox eBirthDate;
    private System.Windows.Forms.PictureBox pictureBox;
    private System.Windows.Forms.Label lbFingerprintOK;
    private System.Windows.Forms.Label lbFingerprintKO;
    private System.Windows.Forms.PictureBox nobodyBox;
    private System.Windows.Forms.Timer tmrBasetime;

    private void InitializeComponent()
    {
        System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fMain));
        this.eFirstName = new System.Windows.Forms.TextBox();
        this.eLastName = new System.Windows.Forms.TextBox();
        this.eBirthDate = new System.Windows.Forms.TextBox();
        this.eStatus = new System.Windows.Forms.TextBox();
        this.pictureBox = new System.Windows.Forms.PictureBox();
        this.lbName = new System.Windows.Forms.Label();
        this.lbBirth = new System.Windows.Forms.Label();
        this.lbDesc = new System.Windows.Forms.Label();
        this.lbUserID = new System.Windows.Forms.Label();
        this.eUserID = new System.Windows.Forms.TextBox();
        this.tmrBasetime = new System.Windows.Forms.Timer();
        this.lbFingerprintOK = new System.Windows.Forms.Label();
        this.lbFingerprintKO = new System.Windows.Forms.Label();
        this.nobodyBox = new System.Windows.Forms.PictureBox();
        this.branded = new System.Windows.Forms.PictureBox();
        this.header = new System.Windows.Forms.PictureBox();
        // 
        // eFirstName
        // 
        this.eFirstName.BackColor = System.Drawing.SystemColors.Control;
        this.eFirstName.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold);
        this.eFirstName.Location = new System.Drawing.Point(0, 64);
        this.eFirstName.ReadOnly = true;
        this.eFirstName.Size = new System.Drawing.Size(240, 20);
        // 
        // eLastName
        // 
        this.eLastName.BackColor = System.Drawing.SystemColors.Control;
        this.eLastName.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold);
        this.eLastName.Location = new System.Drawing.Point(0, 44);
        this.eLastName.ReadOnly = true;
        this.eLastName.Size = new System.Drawing.Size(240, 20);
        // 
        // eBirthDate
        // 
        this.eBirthDate.BackColor = System.Drawing.SystemColors.Control;
        this.eBirthDate.Location = new System.Drawing.Point(0, 104);
        this.eBirthDate.ReadOnly = true;
        this.eBirthDate.Size = new System.Drawing.Size(84, 21);
        // 
        // eStatus
        // 
        this.eStatus.BackColor = System.Drawing.SystemColors.Control;
        this.eStatus.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))));
        this.eStatus.Location = new System.Drawing.Point(0, 248);
        this.eStatus.ReadOnly = true;
        this.eStatus.Size = new System.Drawing.Size(167, 19);
        // 
        // pictureBox
        // 
        this.pictureBox.Location = new System.Drawing.Point(129, 88);
        this.pictureBox.Size = new System.Drawing.Size(102, 136);
        this.pictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
        this.pictureBox.Visible = false;
        // 
        // lbName
        // 
        this.lbName.Location = new System.Drawing.Point(0, 28);
        this.lbName.Size = new System.Drawing.Size(100, 16);
        this.lbName.Text = "Name :";
        // 
        // lbBirth
        // 
        this.lbBirth.Location = new System.Drawing.Point(0, 88);
        this.lbBirth.Size = new System.Drawing.Size(80, 16);
        this.lbBirth.Text = "Date of birth :";
        // 
        // lbDesc
        // 
        this.lbDesc.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold);
        this.lbDesc.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(217)))), ((int)(((byte)(9)))), ((int)(((byte)(27)))));
        this.lbDesc.Location = new System.Drawing.Point(19, 197);
        this.lbDesc.Size = new System.Drawing.Size(97, 27);
        this.lbDesc.Text = "SpringProx SDK sample";
        this.lbDesc.TextAlign = System.Drawing.ContentAlignment.TopCenter;
        // 
        // lbUserID
        // 
        this.lbUserID.Location = new System.Drawing.Point(0, 128);
        this.lbUserID.Size = new System.Drawing.Size(80, 16);
        this.lbUserID.Text = "User ID :";
        // 
        // eUserID
        // 
        this.eUserID.BackColor = System.Drawing.SystemColors.Control;
        this.eUserID.Location = new System.Drawing.Point(0, 144);
        this.eUserID.ReadOnly = true;
        this.eUserID.Size = new System.Drawing.Size(128, 21);
        // 
        // tmrBasetime
        // 
        this.tmrBasetime.Enabled = true;
        this.tmrBasetime.Interval = 250;
        this.tmrBasetime.Tick += new System.EventHandler(this.tmrBasetime_Tick);
        // 
        // lbFingerprintOK
        // 
        this.lbFingerprintOK.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold);
        this.lbFingerprintOK.ForeColor = System.Drawing.Color.Green;
        this.lbFingerprintOK.Location = new System.Drawing.Point(3, 172);
        this.lbFingerprintOK.Size = new System.Drawing.Size(97, 16);
        this.lbFingerprintOK.Text = "Fingerprint OK";
        this.lbFingerprintOK.Visible = false;
        // 
        // lbFingerprintKO
        // 
        this.lbFingerprintKO.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold);
        this.lbFingerprintKO.ForeColor = System.Drawing.Color.OrangeRed;
        this.lbFingerprintKO.Location = new System.Drawing.Point(0, 172);
        this.lbFingerprintKO.Size = new System.Drawing.Size(116, 16);
        this.lbFingerprintKO.Text = "Fingerprint KO";
        this.lbFingerprintKO.Visible = false;
        // 
        // nobodyBox
        // 
        this.nobodyBox.Image = ((System.Drawing.Image)(resources.GetObject("nobodyBox.Image")));
        this.nobodyBox.Location = new System.Drawing.Point(130, 88);
        this.nobodyBox.Size = new System.Drawing.Size(102, 136);
        this.nobodyBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
        // 
        // branded
        // 
        this.branded.Image = ((System.Drawing.Image)(resources.GetObject("branded.Image")));
        this.branded.Location = new System.Drawing.Point(167, 240);
        this.branded.Size = new System.Drawing.Size(67, 30);
        // 
        // header
        // 
        this.header.Image = ((System.Drawing.Image)(resources.GetObject("header.Image")));
        this.header.Location = new System.Drawing.Point(0, 0);
        this.header.Size = new System.Drawing.Size(240, 30);
        // 
        // fMain
        // 
        this.BackColor = System.Drawing.Color.White;
        this.ClientSize = new System.Drawing.Size(234, 270);
        this.Controls.Add(this.header);
        this.Controls.Add(this.branded);
        this.Controls.Add(this.lbFingerprintKO);
        this.Controls.Add(this.lbUserID);
        this.Controls.Add(this.eUserID);
        this.Controls.Add(this.lbDesc);
        this.Controls.Add(this.eFirstName);
        this.Controls.Add(this.lbBirth);
        this.Controls.Add(this.lbName);
        this.Controls.Add(this.eStatus);
        this.Controls.Add(this.eLastName);
        this.Controls.Add(this.eBirthDate);
        this.Controls.Add(this.pictureBox);
        this.Controls.Add(this.lbFingerprintOK);
        this.Controls.Add(this.nobodyBox);
        this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
        this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
        this.MaximizeBox = false;
        this.MinimizeBox = false;
        this.Text = "MyCard Sample App.";
        this.Closed += new System.EventHandler(this.fMain_Closed);
        this.Closing += new System.ComponentModel.CancelEventHandler(this.fMain_Closing);

    }
#endregion

#region Windows Form Glue

    public fMain()
    {
      InitializeComponent();

      workerAllowed = true;
      workerRunning = false;
      workerNextTask = 0;
      workerDoneTask = 0;
      workerResult = false;
      readerActive = false;

      workerEvent = new ManualResetEvent(false);
      workerThread = new Thread(new ThreadStart(workerLoop));
      workerThread.Start();

      workerStart(WORK_OPEN_READER);
    }

    protected override void Dispose(bool disposing)
    {
      base.Dispose(disposing);
    }

    private void tmrBasetime_Tick(object sender, System.EventArgs e)
    {
      if (workerNextTask != 0)
        return;
      if (workerDoneTask != 0)
        return;

      /* Next run in 250ms */
      tmrBasetime.Interval = 250;

      /* Polling */
      workerStart(WORK_FIND_CARD);
    }

    private void fMain_Closing(object sender,
                               System.ComponentModel.CancelEventArgs e)
    {
      int retry = 50;

      workerAllowed = false;
      workerEvent.Set();

      while ((workerRunning) && (retry-- > 0))
        Thread.Sleep(20);
    }

    private void fMain_Closed(object sender, System.EventArgs e)
    {
      if (readerActive)
        SPROX.ReaderClose();
    }

    static void Main()
    {
      Application.Run(new fMain());
    }

#endregion
  }
}
